package tech.autumn.exception.resolver;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import tech.autumn.exception.ExceptionHandler;
import tech.autumn.spec.annotation.Named;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 业务异常处理器
 * @author koko
 * @date 2025年01月21日 15:00
 */
public class HandlerAllExceptionResolver extends AbstractHandlerExceptionJsonResolver {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final ExceptionHandler exceptionHandler;

    public HandlerAllExceptionResolver(@Nullable ExceptionHandler exceptionHandler) {
        super();
        this.exceptionHandler = exceptionHandler;
    }

    private String handleException(HttpServletRequest request, Object handler,
                                   Exception ex) {
        if(exceptionHandler != null) {
            return exceptionHandler.handle(getDisplayName(handler), ex, request);
        }else {
            return RandomStringUtils.randomNumeric(16);
        }
    }

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                                         Exception ex) {
        String errorId = handleException(request, handler, ex);
        String msg = String.format("服务异常[%s]", errorId);
        if(logger.isErrorEnabled()) {
            logger.error(msg, ex);
        }
        return fail(HttpStatus.INTERNAL_SERVER_ERROR, msg);
    }

    private String getDisplayName(Object handler) {
        if (!(handler instanceof HandlerMethod)) {
            return null;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        String methodName = getMethodName(handlerMethod);
        if (methodName != null) {
            String beanName = getBeanName(handlerMethod);
            if (beanName != null) {
                return beanName + '-' + methodName;
            } else {
                return methodName;
            }
        }
        return null;
    }

    private String getMethodName(HandlerMethod handlerMethod) {
        Named named = handlerMethod.getMethodAnnotation(Named.class);
        if (named != null && StringUtils.hasText(named.value())) {
            return named.value();
        }
        RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
        if (requestMapping != null && StringUtils.hasText(requestMapping.name())) {
            return requestMapping.name();
        }
        ApiOperation apiOperation = handlerMethod.getMethodAnnotation(ApiOperation.class);
        if (apiOperation != null && StringUtils.hasText(apiOperation.value())) {
            return apiOperation.value();
        }
        return null;
    }

    private String getBeanName(HandlerMethod handlerMethod) {
        Named named = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), Named.class);
        if (named != null && StringUtils.hasText(named.value())) {
            return named.value();
        }
        RequestMapping beanMapping = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(),
                RequestMapping.class);
        if (beanMapping != null && StringUtils.hasText(beanMapping.name())) {
            return beanMapping.name();
        }
        Api api = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(),
                Api.class);
        if (api != null && api.tags() != null && api.tags().length > 0) {
            return api.tags()[0];
        }
        return null;
    }
}
